# app/core/parser.py
import os
import json
import sys
import re
import hashlib
import time
import datetime
import base64
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from openai import OpenAI, APIError, APIConnectionError, RateLimitError
from dotenv import load_dotenv

# 确保加载 .env 文件
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(os.path.dirname(current_dir))
env_path = os.path.join(project_root, '.env')

print(f"尝试从 {env_path} 加载环境变量...")
if os.path.exists(env_path):
    load_dotenv(env_path)
    print("成功加载 .env 文件")
else:
    print("警告: .env 文件不存在于路径: " + env_path)
    # 尝试从当前目录加载
    load_dotenv()
    print("尝试从当前目录加载 .env 文件")

# 从环境变量获取 DeepSeek API配置
deepseek_api_key = os.getenv("DEEPSEEK_API_KEY")
deepseek_api_base_url = os.getenv("DEEPSEEK_API_BASE", "https://api.deepseek.com/v1")

# 从环境变量获取豆包 API配置
doubao_api_key = os.getenv("DOUBAO_API_KEY")
doubao_api_base_url = os.getenv("DOUBAO_API_BASE", "https://ark.cn-beijing.volces.com/api/v3")

# 检查可用的API配置
available_models = []
if deepseek_api_key:
    available_models.append("deepseek")
    print(f"成功获取 DeepSeek API Key: {deepseek_api_key[:4]}...{deepseek_api_key[-4:]}")
    print(f"DeepSeek API Base URL: {deepseek_api_base_url}")

if doubao_api_key:
    available_models.append("doubao")
    print(f"成功获取 豆包 API Key: {doubao_api_key[:4]}...{doubao_api_key[-4:]}")
    print(f"豆包 API Base URL: {doubao_api_base_url}")

if not available_models:
    print("错误：未设置任何有效的 API Key。")
    print("请在 .env 文件中添加以下任一配置：")
    print("  DEEPSEEK_API_KEY=你的DeepSeek API密钥")
    print("  DOUBAO_API_KEY=你的豆包 API密钥")
    raise ValueError("错误：未设置任何有效的 API Key。")
else:
    print(f"可用的AI模型: {', '.join(available_models)}")

# --- 加密工具类 ---
class CacheEncryption:
    """缓存数据加密工具类"""
    def __init__(self, encryption_key=None):
        """
        初始化加密工具。
        
        Args:
            encryption_key: 加密密钥，如果为None则使用环境变量或生成新密钥
        """
        self.encryption_key = encryption_key or os.getenv("ADDRESS_CACHE_ENCRYPTION_KEY")
        
        # 如果没有指定密钥也没有环境变量，则生成一个新密钥
        if not self.encryption_key:
            # 生成一个随机的盐值
            salt = os.urandom(16)
            # 使用PBKDF2HMAC生成密钥
            kdf = PBKDF2HMAC(
                algorithm=hashes.SHA256(),
                length=32,
                salt=salt,
                iterations=100000,
            )
            # 使用应用名称作为密码派生密钥
            app_secret = b"parse-smart-address-app-secret"
            key = base64.urlsafe_b64encode(kdf.derive(app_secret))
            self.encryption_key = key.decode('utf-8')
            
            print("警告: 未设置缓存加密密钥，已自动生成一个临时密钥")
            print(f"建议在 .env 文件中添加: ADDRESS_CACHE_ENCRYPTION_KEY={self.encryption_key}")
        
        # 初始化Fernet加密器
        self.cipher = Fernet(self.encryption_key.encode() if isinstance(self.encryption_key, str) else self.encryption_key)
    
    def encrypt(self, data):
        """
        加密数据
        
        Args:
            data: 要加密的数据(字典或字符串)
            
        Returns:
            加密后的字符串
        """
        if isinstance(data, dict):
            # 转换为JSON字符串再加密
            json_str = json.dumps(data, ensure_ascii=False)
            return self.encrypt(json_str)
        elif isinstance(data, str):
            # 加密字符串
            return self.cipher.encrypt(data.encode('utf-8')).decode('utf-8')
        else:
            raise TypeError("不支持的数据类型，只能加密字典或字符串")
    
    def decrypt(self, encrypted_data):
        """
        解密数据
        
        Args:
            encrypted_data: 加密的字符串
            
        Returns:
            解密后的原始数据(字典或字符串)
        """
        try:
            # 解密字符串
            decrypted = self.cipher.decrypt(encrypted_data.encode('utf-8')).decode('utf-8')
            
            # 尝试解析JSON
            try:
                return json.loads(decrypted)
            except json.JSONDecodeError:
                # 不是有效的JSON，返回原始字符串
                return decrypted
                
        except Exception as e:
            print(f"解密失败: {e}")
            return None

# --- 缓存管理类 ---
class AddressCache:
    """地址解析结果的缓存管理。"""
    def __init__(self, cache_file=None, max_age_days=7, encryption_key=None):
        """
        初始化缓存管理器。
        
        Args:
            cache_file: 缓存文件路径，如果为None则使用内存缓存
            max_age_days: 缓存数据的最大保留天数，默认7天
            encryption_key: 加密密钥，如果为None则使用环境变量或生成新密钥
        """
        self.cache = {}  # 内存缓存，格式: {key: {'data': result, 'timestamp': unix_timestamp}}
        self.cache_file = cache_file
        self.cache_hits = 0
        self.cache_misses = 0
        self.max_age_seconds = max_age_days * 24 * 60 * 60  # 转换为秒
        self.last_cleanup_time = 0
        
        # 初始化加密工具
        self.encryption = CacheEncryption(encryption_key)
        
        # 如果指定了缓存文件且文件存在，则加载缓存
        if self.cache_file and os.path.exists(self.cache_file):
            try:
                with open(self.cache_file, 'r', encoding='utf-8') as f:
                    encrypted_cache = json.load(f)
                
                # 解密缓存数据
                self._decrypt_cache(encrypted_cache)
                
                # 执行清理操作，移除过期数据
                self._cleanup_expired_cache()
                valid_entries = sum(1 for v in self.cache.values() if isinstance(v, dict) and 'data' in v)
                print(f"已从文件 {self.cache_file} 加载并解密 {valid_entries} 条有效缓存记录")
            except Exception as e:
                print(f"加载或解密缓存文件时出错: {e}")
                self.cache = {}
    
    def _decrypt_cache(self, encrypted_cache):
        """解密缓存数据"""
        self.cache = {}  # 重置缓存
        
        # 处理旧格式的缓存文件(未加密)
        if "is_encrypted" not in encrypted_cache:
            print("检测到旧版未加密缓存，将进行格式转换")
            self.cache = encrypted_cache
            return
            
        # 处理加密的缓存
        if encrypted_cache.get("is_encrypted") != True:
            print("警告: 缓存文件格式异常，将重置缓存")
            return
            
        # 解密每个缓存条目
        for key, encrypted_value in encrypted_cache.get("data", {}).items():
            try:
                decrypted_value = self.encryption.decrypt(encrypted_value)
                if decrypted_value:
                    self.cache[key] = decrypted_value
            except Exception as e:
                print(f"解密缓存条目失败 (key={key[:8]}...): {e}")
    
    def _encrypt_cache_for_storage(self):
        """为存储准备加密的缓存数据"""
        encrypted_cache = {
            "is_encrypted": True,
            "version": 1,
            "encrypted_at": time.time(),
            "data": {}
        }
        
        # 加密每个缓存条目
        for key, value in self.cache.items():
            try:
                encrypted_cache["data"][key] = self.encryption.encrypt(value)
            except Exception as e:
                print(f"加密缓存条目失败 (key={key[:8]}...): {e}")
                
        return encrypted_cache
    
    def _get_key(self, address):
        """为地址生成唯一的缓存键"""
        # 清理地址字符串(去除多余空格等)
        cleaned_address = re.sub(r'\s+', ' ', address).strip()
        # 使用MD5生成哈希值作为键
        return hashlib.md5(cleaned_address.encode('utf-8')).hexdigest()
    
    def _cleanup_expired_cache(self):
        """清理过期的缓存数据"""
        now = time.time()
        # 每次运行只执行一次清理，避免频繁IO操作
        if now - self.last_cleanup_time < 3600:  # 每小时最多清理一次
            return
            
        self.last_cleanup_time = now
        expired_time = now - self.max_age_seconds
        expired_keys = []
        
        # 统计清理前的缓存数量
        original_count = len(self.cache)
        
        # 检查所有缓存项
        for key, value in list(self.cache.items()):
            # 兼容旧缓存格式
            if not isinstance(value, dict) or 'timestamp' not in value:
                # 转换旧格式缓存或删除无效数据
                if isinstance(value, dict):
                    self.cache[key] = {'data': value, 'timestamp': now}
                else:
                    expired_keys.append(key)
                continue
                
            # 检查是否过期
            if value['timestamp'] < expired_time:
                expired_keys.append(key)
        
        # 删除过期的缓存项
        for key in expired_keys:
            del self.cache[key]
        
        # 如果有删除操作且使用文件缓存，则保存更新后的缓存
        if expired_keys and self.cache_file:
            try:
                # 加密并保存缓存
                self._save_cache_to_file()
                print(f"缓存清理完成: 删除了 {len(expired_keys)} 条过期记录, 从 {original_count} 条减少到 {len(self.cache)} 条")
            except Exception as e:
                print(f"保存清理后的缓存时出错: {e}")
    
    def _save_cache_to_file(self):
        """加密并保存缓存到文件"""
        if not self.cache_file:
            return
            
        try:
            # 生成加密的缓存数据
            encrypted_cache = self._encrypt_cache_for_storage()
            
            # 保存到文件
            with open(self.cache_file, 'w', encoding='utf-8') as f:
                json.dump(encrypted_cache, f, ensure_ascii=False, indent=2)
            print(f"已加密并保存缓存到文件 {self.cache_file}")
        except Exception as e:
            print(f"加密并保存缓存到文件时出错: {e}")
    
    def get(self, address):
        """
        从缓存获取地址解析结果。
        
        Args:
            address: 原始地址字符串
            
        Returns:
            缓存的解析结果，如果缓存中不存在则返回None
        """
        # 定期清理过期缓存
        self._cleanup_expired_cache()
        
        key = self._get_key(address)
        cache_entry = self.cache.get(key)
        
        # 处理不同缓存格式
        if not cache_entry:
            self.cache_misses += 1
            print(f"缓存未命中 - 地址: {address[:30]}...")
            return None
        
        # 兼容旧格式缓存
        if isinstance(cache_entry, dict) and 'data' in cache_entry:
            result = cache_entry['data']
            # 更新访问时间
            self.cache[key]['timestamp'] = time.time()
        else:
            # 旧格式缓存，直接作为结果返回，同时更新为新格式
            result = cache_entry
            self.cache[key] = {'data': result, 'timestamp': time.time()}
        
        self.cache_hits += 1
        print(f"缓存命中 - 地址: {address[:30]}...")
        return result
    
    def set(self, address, result):
        """
        将解析结果存入缓存。
        
        Args:
            address: 原始地址字符串
            result: 解析结果(字典)
        """
        # 只缓存成功的解析结果(不包含error字段的)
        if not result or "error" in result:
            print("解析结果包含错误，不缓存")
            return
        
        key = self._get_key(address)
        # 使用新的缓存格式存储
        self.cache[key] = {
            'data': result,
            'timestamp': time.time()
        }
        print(f"已缓存解析结果 - 地址: {address[:30]}...")
        
        # 如果指定了缓存文件，则保存到文件
        if self.cache_file:
            self._save_cache_to_file()
    
    def force_cleanup(self):
        """强制执行缓存清理，不考虑时间间隔限制"""
        now = time.time()
        self.last_cleanup_time = 0  # 重置上次清理时间
        self._cleanup_expired_cache()
        return {
            "cleaned_at": datetime.datetime.fromtimestamp(now).strftime('%Y-%m-%d %H:%M:%S'),
            "cache_size": len(self.cache)
        }
    
    def stats(self):
        """返回缓存统计信息"""
        total = self.cache_hits + self.cache_misses
        hit_rate = self.cache_hits / total * 100 if total > 0 else 0
        
        # 计算缓存数据的年龄分布
        now = time.time()
        age_distribution = {
            "1_day": 0,
            "3_days": 0,
            "7_days": 0,
            "older": 0
        }
        
        for entry in self.cache.values():
            if isinstance(entry, dict) and 'timestamp' in entry:
                age_in_days = (now - entry['timestamp']) / (24 * 60 * 60)
                if age_in_days <= 1:
                    age_distribution["1_day"] += 1
                elif age_in_days <= 3:
                    age_distribution["3_days"] += 1
                elif age_in_days <= 7:
                    age_distribution["7_days"] += 1
                else:
                    age_distribution["older"] += 1
        
        return {
            "hits": self.cache_hits,
            "misses": self.cache_misses,
            "total": total,
            "hit_rate": f"{hit_rate:.2f}%",
            "cache_size": len(self.cache),
            "age_distribution": age_distribution,
            "max_age_days": self.max_age_seconds / (24 * 60 * 60),
            "is_encrypted": True
        }

# --- AI 模型接口定义 (为了未来扩展) ---
class AIParserInterface:
    """定义 AI 解析器的通用接口，方便未来切换模型。"""
    def parse(self, address: str) -> dict:
        raise NotImplementedError
        
    def get_cache_stats(self) -> dict:
        """获取缓存统计信息"""
        raise NotImplementedError
        
    def force_cleanup_cache(self) -> dict:
        """强制清理过期缓存"""
        raise NotImplementedError

# --- 豆包 解析器实现 ---
class DoubaoParser(AIParserInterface):
    """使用 豆包 API 解析地址的实现。"""
    def __init__(self, api_key: str, base_url: str, model_name: str = "doubao-seed-1-6-flash-250615", cache_file=None, cache_max_age_days=7, encryption_key=None):
        self.client = OpenAI(api_key=api_key, base_url=base_url)
        self.model = model_name
        # 初始化缓存
        self.cache = AddressCache(cache_file, max_age_days=cache_max_age_days, encryption_key=encryption_key)

    def parse(self, address: str) -> dict:
        """
        调用 豆包 API 解析地址。

        Args:
            address: 原始地址字符串。

        Returns:
            包含解析结果或错误信息的字典。
        """
        # 首先检查缓存
        cached_result = self.cache.get(address)
        if cached_result:
            print("使用缓存结果，避免调用 API")
            return cached_result
            
        # 缓存中没有，调用 API 进行解析
        prompt = self._build_prompt(address)
        system_prompt = """
你是一个专业的地址解析助手。你的任务是将用户提供的非结构化地址文本解析成结构化的 JSON 对象。
JSON 对象应包含以下字段：
- "province": 省份 (例如: "广东省", "北京市")
- "city": 城市 (例如: "深圳市", "北京市")
- "district": 区/县 (例如: "南山区", "朝阳区")
- "street": 街道/镇 (例如: "粤海街道", "三里屯街道")
- "address_detail": 详细地址 (包括门牌号、小区、楼栋等，不含省市区街道)
- "name": 收件人姓名 (如果地址中包含)
- "mobile": 收件人手机号码，包括普通手机号(如13800138000)、虚拟号码(如14749871654-8722)和国际格式号码(如86-138-00000000)，多个手机号码用逗号分隔
- "telephone": 收件人固定电话 (例如：0755-12345678、400-0211880 等座机号码)

特别说明：
1. 如果地址信息中缺少某个字段，请将对应的值设为 null 或空字符串 ""
2. 请区分手机号码(mobile)与固定电话(telephone)：
   - mobile：包括以下几种情况：
     * 11位数字，通常以13/14/15/16/17/18/19开头的手机号码，如 13800138000
     * 虚拟号码：通常为手机号后带分机号，如 14749871654-8722
     * 国际格式的号码：如 86-138-00000000
   - telephone：仅包括以下情况：
     * 座机号码：通常格式为区号-号码，如 0755-12345678
     * 400/800 电话：如 400-0211880

请确保只输出符合上述格式的 JSON 对象，不要包含任何额外的解释或说明文字。
如果无法解析地址，请返回一个包含 "error" 字段的 JSON 对象，例如：{"error": "无法解析地址"}。
"""

        try:
            print(f"正在发送解析请求到 豆包 API，地址内容: {address[:30]}...")
            # 记录开始时间
            start_time = time.time()
            
            response = self.client.chat.completions.create(
                model=self.model,
                messages=[
                    {"role": "system", "content": system_prompt},
                    {"role": "user", "content": prompt}
                ],
                response_format={"type": "json_object"}, # 请求 JSON 输出
                temperature=0.0, # 低温确保输出更稳定和结构化
                max_tokens=500
            )
            
            # 计算并输出API调用耗时
            elapsed_time = time.time() - start_time
            print(f"豆包 API 调用耗时: {elapsed_time:.2f} 秒")

            # 提取并解析 JSON 响应
            content = response.choices[0].message.content
            if content:
                try:
                    print(f"收到 API 响应: {content[:100]}...")
                    parsed_json = json.loads(content.strip())
                    # 可以在这里添加对 JSON 结构的校验
                    required_keys = ["province", "city", "district", "street", "address_detail", "name", "mobile", "telephone"]
                    # 基本校验，确保包含关键字段 (允许值为 null 或 "")
                    if all(key in parsed_json for key in required_keys):
                         print("解析成功，结构验证通过")
                         # 存入缓存
                         self.cache.set(address, parsed_json)
                         return parsed_json
                    elif "error" in parsed_json:
                         print(f"API 返回错误: {parsed_json['error']}")
                         return parsed_json # 返回模型报告的错误
                    else:
                         # 模型返回了JSON但格式不完全符合预期
                         print(f"警告：模型返回的 JSON 结构不完整: {parsed_json}")
                         # 尝试填充缺失的键
                         for key in required_keys:
                             if key not in parsed_json:
                                 parsed_json[key] = None # 或 ""
                         # 存入缓存
                         self.cache.set(address, parsed_json)
                         return parsed_json

                except json.JSONDecodeError:
                    print(f"错误：无法解析来自 AI 的 JSON 响应: {content}")
                    return {"error": f"AI响应格式错误: {content}"}
            else:
                print("API 未返回有效内容")
                return {"error": "AI 未返回有效内容"}

        except APIConnectionError as e:
            print(f"错误：无法连接到 豆包 API: {e}")
            return {"error": "无法连接到 AI 服务"}
        except RateLimitError as e:
            print(f"错误：豆包 API 请求频率超限: {e}")
            return {"error": "AI 服务请求频率超限"}
        except APIError as e:
            print(f"错误：豆包 API 返回错误: {e.status_code} {e.response}")
            return {"error": f"AI 服务内部错误: {e.message}"}
        except Exception as e:
            print(f"解析地址时发生未知错误: {e}")
            return {"error": f"处理请求时发生内部错误: {str(e)}"}

    def _build_prompt(self, address: str) -> str:
        """构建发送给 AI 的用户提示。"""
        return f"""请将以下地址解析为 JSON 对象，注意虚拟号码如"14749871654-8722"和国际格式的号码如"86-138-00000000"应放在mobile字段中：

{address}

示例：对于地址"广东省广州市海珠区赤岗街道测试专用，张三，13800138000，0755-12345678"，应返回：
{{
  "province": "广东省",
  "city": "广州市",
  "district": "海珠区",
  "street": "赤岗街道",
  "address_detail": "测试专用",
  "name": "张三",
  "mobile": "13800138000",
  "telephone": "0755-12345678"
}}

示例2："测试，14749871654-8722，86-138-00000000，广东省 广州市 海珠区 赤岗街道 测试专用测试专用 ，000000"
应返回：
{{
  "province": "广东省",
  "city": "广州市",
  "district": "海珠区",
  "street": "赤岗街道",
  "address_detail": "测试专用测试专用",
  "name": "测试",
  "mobile": "14749871654-8722,86-138-00000000",
  "telephone": ""
}}"""

    def get_cache_stats(self):
        """获取缓存统计信息"""
        return self.cache.stats()
        
    def force_cleanup_cache(self):
        """强制清理过期缓存"""
        return self.cache.force_cleanup()

# --- DeepSeek 解析器实现 ---
class DeepSeekParser(AIParserInterface):
    """使用 DeepSeek API 解析地址的实现。"""
    def __init__(self, api_key: str, base_url: str, cache_file=None, cache_max_age_days=7, encryption_key=None):
        self.client = OpenAI(api_key=api_key, base_url=base_url)
        self.model = "deepseek-chat" # 或者选择其他合适的 DeepSeek 模型
        # 初始化缓存
        self.cache = AddressCache(cache_file, max_age_days=cache_max_age_days, encryption_key=encryption_key)

    def parse(self, address: str) -> dict:
        """
        调用 DeepSeek API 解析地址。

        Args:
            address: 原始地址字符串。

        Returns:
            包含解析结果或错误信息的字典。
        """
        # 首先检查缓存
        cached_result = self.cache.get(address)
        if cached_result:
            print("使用缓存结果，避免调用 API")
            return cached_result
            
        # 缓存中没有，调用 API 进行解析
        prompt = self._build_prompt(address)
        system_prompt = """
你是一个专业的地址解析助手。你的任务是将用户提供的非结构化地址文本解析成结构化的 JSON 对象。
JSON 对象应包含以下字段：
- "province": 省份 (例如: "广东省", "北京市")
- "city": 城市 (例如: "深圳市", "北京市")
- "district": 区/县 (例如: "南山区", "朝阳区")
- "street": 街道/镇 (例如: "粤海街道", "三里屯街道")
- "address_detail": 详细地址 (包括门牌号、小区、楼栋等，不含省市区街道)
- "name": 收件人姓名 (如果地址中包含)
- "mobile": 收件人手机号码，包括普通手机号(如13800138000)、虚拟号码(如14749871654-8722)和国际格式号码(如86-138-00000000)，多个手机号码用逗号分隔
- "telephone": 收件人固定电话 (例如：0755-12345678、400-0211880 等座机号码)

特别说明：
1. 如果地址信息中缺少某个字段，请将对应的值设为 null 或空字符串 ""
2. 请区分手机号码(mobile)与固定电话(telephone)：
   - mobile：包括以下几种情况：
     * 11位数字，通常以13/14/15/16/17/18/19开头的手机号码，如 13800138000
     * 虚拟号码：通常为手机号后带分机号，如 14749871654-8722
     * 国际格式的号码：如 86-138-00000000
   - telephone：仅包括以下情况：
     * 座机号码：通常格式为区号-号码，如 0755-12345678
     * 400/800 电话：如 400-0211880

请确保只输出符合上述格式的 JSON 对象，不要包含任何额外的解释或说明文字。
如果无法解析地址，请返回一个包含 "error" 字段的 JSON 对象，例如：{"error": "无法解析地址"}。
"""

        try:
            print(f"正在发送解析请求到 DeepSeek API，地址内容: {address[:30]}...")
            # 记录开始时间
            start_time = time.time()
            
            response = self.client.chat.completions.create(
                model=self.model,
                messages=[
                    {"role": "system", "content": system_prompt},
                    {"role": "user", "content": prompt}
                ],
                response_format={"type": "json_object"}, # 请求 JSON 输出
                temperature=0.0, # 低温确保输出更稳定和结构化
                max_tokens=500
            )
            
            # 计算并输出API调用耗时
            elapsed_time = time.time() - start_time
            print(f"DeepSeek API 调用耗时: {elapsed_time:.2f} 秒")

            # 提取并解析 JSON 响应
            content = response.choices[0].message.content
            if content:
                try:
                    print(f"收到 API 响应: {content[:100]}...")
                    parsed_json = json.loads(content.strip())
                    # 可以在这里添加对 JSON 结构的校验
                    required_keys = ["province", "city", "district", "street", "address_detail", "name", "mobile", "telephone"]
                    # 基本校验，确保包含关键字段 (允许值为 null 或 "")
                    if all(key in parsed_json for key in required_keys):
                         print("解析成功，结构验证通过")
                         # 存入缓存
                         self.cache.set(address, parsed_json)
                         return parsed_json
                    elif "error" in parsed_json:
                         print(f"API 返回错误: {parsed_json['error']}")
                         return parsed_json # 返回模型报告的错误
                    else:
                         # 模型返回了JSON但格式不完全符合预期
                         print(f"警告：模型返回的 JSON 结构不完整: {parsed_json}")
                         # 尝试填充缺失的键
                         for key in required_keys:
                             if key not in parsed_json:
                                 parsed_json[key] = None # 或 ""
                         # 存入缓存
                         self.cache.set(address, parsed_json)
                         return parsed_json

                except json.JSONDecodeError:
                    print(f"错误：无法解析来自 AI 的 JSON 响应: {content}")
                    return {"error": f"AI响应格式错误: {content}"}
            else:
                print("API 未返回有效内容")
                return {"error": "AI 未返回有效内容"}

        except APIConnectionError as e:
            print(f"错误：无法连接到 DeepSeek API: {e}")
            return {"error": "无法连接到 AI 服务"}
        except RateLimitError as e:
            print(f"错误：DeepSeek API 请求频率超限: {e}")
            return {"error": "AI 服务请求频率超限"}
        except APIError as e:
            print(f"错误：DeepSeek API 返回错误: {e.status_code} {e.response}")
            return {"error": f"AI 服务内部错误: {e.message}"}
        except Exception as e:
            print(f"解析地址时发生未知错误: {e}")
            return {"error": f"处理请求时发生内部错误: {str(e)}"}

    def _build_prompt(self, address: str) -> str:
        """构建发送给 AI 的用户提示。"""
        return f"""请将以下地址解析为 JSON 对象，注意虚拟号码如"14749871654-8722"和国际格式的号码如"86-138-00000000"应放在mobile字段中：

{address}

示例：对于地址"广东省广州市海珠区赤岗街道测试专用，张三，13800138000，0755-12345678"，应返回：
{{
  "province": "广东省",
  "city": "广州市",
  "district": "海珠区",
  "street": "赤岗街道",
  "address_detail": "测试专用",
  "name": "张三",
  "mobile": "13800138000",
  "telephone": "0755-12345678"
}}

示例2："测试，14749871654-8722，86-138-00000000，广东省 广州市 海珠区 赤岗街道 测试专用测试专用 ，000000"
应返回：
{{
  "province": "广东省",
  "city": "广州市",
  "district": "海珠区",
  "street": "赤岗街道",
  "address_detail": "测试专用测试专用",
  "name": "测试",
  "mobile": "14749871654-8722,86-138-00000000",
  "telephone": ""
}}"""

    def get_cache_stats(self):
        """获取缓存统计信息"""
        return self.cache.stats()
        
    def force_cleanup_cache(self):
        """强制清理过期缓存"""
        return self.cache.force_cleanup()

# --- 获取解析器实例 ---
def get_parser(model_type: str = None) -> AIParserInterface:
    """
    获取地址解析器的实例。
    
    Args:
        model_type: 指定要使用的模型类型（'deepseek', 'doubao'），如果为None则根据环境变量自动选择
        
    Returns:
        对应的解析器实例
    """
    # 从环境变量获取配置
    cache_max_age_days = int(os.getenv("ADDRESS_CACHE_MAX_AGE_DAYS", "7"))
    encryption_key = os.getenv("ADDRESS_CACHE_ENCRYPTION_KEY")
    
    # 设置缓存文件路径
    cache_dir = os.path.join(project_root, "cache")
    # 确保缓存目录存在
    if not os.path.exists(cache_dir):
        try:
            os.makedirs(cache_dir)
            print(f"创建缓存目录: {cache_dir}")
        except Exception as e:
            print(f"创建缓存目录失败: {e}")
            cache_dir = None
    
    cache_file = os.path.join(cache_dir, "address_cache.json") if cache_dir else None
    
    # 确定要使用的模型类型
    if model_type is None:
        # 从环境变量读取模型偏好设置
        model_type = os.getenv("AI_MODEL_TYPE", "").lower()
        
        # 如果没有设置偏好，则按优先级选择可用模型
        if not model_type:
            if "doubao" in available_models:
                model_type = "doubao"
                print("未指定模型类型，自动选择豆包模型")
            elif "deepseek" in available_models:
                model_type = "deepseek"  
                print("未指定模型类型，自动选择DeepSeek模型")
            else:
                raise ValueError("没有可用的AI模型配置，请检查环境变量")
    
    # 根据模型类型实例化对应的解析器
    if model_type == "doubao":
        if doubao_api_key is not None:
            print("实例化 豆包Parser...")
            doubao_model_name = os.getenv("DOUBAO_MODEL_NAME", "doubao-seed-1-6-flash-250615")
            print(f"缓存设置: 最大保留天数 = {cache_max_age_days} 天, 加密存储 = {'是' if encryption_key else '自动生成密钥'}")
            return DoubaoParser(
                api_key=doubao_api_key,
                base_url=doubao_api_base_url or "https://ark.cn-beijing.volces.com/api/v3",
                model_name=doubao_model_name,
                cache_file=cache_file,
                cache_max_age_days=cache_max_age_days,
                encryption_key=encryption_key
            )
        else:
            raise ValueError("豆包API密钥未配置，请在.env文件中设置DOUBAO_API_KEY")
            
    elif model_type == "deepseek":
        if deepseek_api_key is not None:
            print("实例化 DeepSeekParser...")
            print(f"缓存设置: 最大保留天数 = {cache_max_age_days} 天, 加密存储 = {'是' if encryption_key else '自动生成密钥'}")
            return DeepSeekParser(
                api_key=deepseek_api_key,
                base_url=deepseek_api_base_url or "https://api.deepseek.com/v1",
                cache_file=cache_file,
                cache_max_age_days=cache_max_age_days,
                encryption_key=encryption_key
            )
        else:
            raise ValueError("DeepSeek API密钥未配置，请在.env文件中设置DEEPSEEK_API_KEY")
    else:
        raise ValueError(f"不支持的模型类型: {model_type}，支持的类型有: deepseek, doubao")

# --- 示例用法 (用于直接运行此文件测试) ---
if __name__ == '__main__':
    print("直接运行 parser.py 进行测试...")
    parser = get_parser()
    
    # 测试地址列表
    test_addresses = [
        "广东省深圳市南山区粤海街道科技南十二路 中科纳能大厦 A座 3楼 张三 13800138000",
        "北京市朝阳区三里屯街道 某小区 10号楼 1单元 101室 李四 13912345678",
        "测试，14749871654-8722，86-138-00000000，广东省 广州市 海珠区 赤岗街道 测试专用测试专用 ，000000",
        "江苏省南京市玄武区孝陵卫街道200号南京理工大学 钱七 188xxxxxxxx 0579-85138378",
        "四川省成都市武侯区人民南路四段1号成都数码广场8楼F5 赵九 手机：17799998888 座机：028-12345678",
        "重庆市渝中区解放碑街道时代天街25号 李先生 400-0211880",
        "广东省广州市天河区体育西路101号 王芳 020-38889999 13522223333"
    ]
    
    print("=== 测试多种地址格式的解析 ===")
    for i, addr in enumerate(test_addresses):
        print(f"\n[测试 {i+1}] 解析地址: {addr}")
        result = parser.parse(addr)
        print("解析结果:")
        print(json.dumps(result, indent=4, ensure_ascii=False))
    
    # 重复解析第一个地址，测试缓存效果
    print("\n=== 测试缓存效果 ===")
    print(f"重新解析第一个地址: {test_addresses[0]}")
    result = parser.parse(test_addresses[0])
    print("解析结果:")
    print(json.dumps(result, indent=4, ensure_ascii=False))
    
    # 显示缓存统计
    print("\n=== 缓存统计 ===")
    stats = parser.get_cache_stats()
    print(json.dumps(stats, indent=4, ensure_ascii=False))
    
    # 测试强制清理缓存
    print("\n=== 测试强制清理缓存 ===")
    cleanup_result = parser.force_cleanup_cache()
    print(f"清理结果: {json.dumps(cleanup_result, indent=4, ensure_ascii=False)}")
    
    print("\n=== 缓存加密测试 ===")
    print("查看缓存文件内容，确认数据已加密存储")
    
    print("\n所有测试完成!") 